require 'particle'

class Missile
  attr_reader :unit, :x, :y, :width, :height, :color, :angle, :speedx, :speedy, :damage, :splash_radius, :free_spinning, :affected_by_gravity, :dies, :lifetime, :lethality_time, :poison
  WIDTH = 0.5
  HEIGHT = 2
  GrenadeLauncher_SPLASH_RADIUS = 40
  def initialize(window, unit, x, y, speed, angle, color = 0xffffffff)
    @window, @unit, @x, @y, @angle, @color = window, unit, x, y, angle, color
    @map = window.map
    @dead, @lifetime = false, 0
    @dies_on_hit = true
    @speedx = offset_x(angle, speed) + unit.dx
    @speedy = -offset_y(angle, speed) - unit.dy
    $game.missiles << self
  end
  def update
    @lifetime += 1
    if @free_spinning
      begin
        @angle += @spin
      rescue
        DebugOut.out self.class.to_s
        DebugOut.out $!
        raise
      end
    else
      @angle = Math.atan2(@speedx, @speedy) / 3.1415926 * 180
    end
    @speedy -= Game::GRAVITY if @affected_by_gravity
    
    max_ds = [@speedx.abs, @speedy.abs].max.to_i
    if @speedx.abs > @speedy.abs then max_d = :x else max_d = :y end
    max_ds.times do
      prev_y = @y
      case max_d
        when :x
          @x += @speedx <=> 0
          if @speedx == 0
            @y += @speedy <=> 0
          else
            @y += (@speedy / @speedx).abs * (@speedy <=> 0)
          end
        when :y
          @y += @speedy <=> 0
          if @speedy == 0
            @x += @speedx <=> 0
          else
            @x += (@speedx / @speedy).abs * (@speedx <=> 0)
          end
      end
      if @explodes_on_timer and @lifetime >= @explode_time
        die
      end
      if land? or (!@explodes_on_timer and hit_unit?)
        if @dies and @landings == 0 and !@explodes_on_timer
          die
        elsif !@dies or (@explodes_on_timer and @landings == 0)
          @y = prev_y
          @speedx = @spin = 0
        end
        break
      end
    end
    
    unless @dies_on_hit
      @window.missile_splash_hit(self) if @lifetime % @hit_frequency == 0
      if @explodes_on_timer and @lifetime >= @explode_time
        die
      end
    end
    
    disappear if off_screen?
  end
  def land?
    if !@map.would_fit(@x, @y, @width, @height)
      if @landings and @landings > 0
        ground_angle = $game.map.get_angle(@x, @y)
        missile_angle = Gosu.angle(0, 0, @speedx, -@speedy)
        missile_angle -= 360 if missile_angle > 180
        ground_angle_diff_from_90 = ground_angle - 90
        missile_angle_adjusted = missile_angle - ground_angle_diff_from_90
        new_angle = -missile_angle_adjusted + ground_angle_diff_from_90
        new_speed = distance(0, 0, @speedx, @speedy) * @bounciness
        @speedx = offset_x(-new_angle, new_speed)
        @speedy = offset_y(new_angle, new_speed)
        #to avoid it getting stuck
        @x += (@speedx <=> 0) * 5
        @y += (@speedy <=> 0) * 5
        @landings -= 1
        @speedx = @speedy = 0 if @landings == 0
        return true
      else
        @unit.notify_landing(self)
        true
      end
    end
  end
  def off_screen?
    if @x > @window.width || @x < 1 or @y < 1 or (!@affected_by_gravity and @y > $game.height and @angle == 0)
      DebugOut.out "#{self.class.to_s} flew off screen"
      return true
    end
    if !@affected_by_gravity and @y < 0
      @unit.notify_off_screen(self)
      return true
    end
  end
  def hit_unit?
    if @dies_on_hit and (@lethality_time == nil or @lifetime >= @lethality_time)
      return @window.hit?(self, @x, @y)
    end
    false
  end
  def die
    if !@dead
      @window.play(@hit_sound, @hit_sound_length) if defined?(@hit_sound_length) #can't play it here or the sound gets cut off when the missile is removed
      @window.play(@hit_sound) if @hit_sound and !defined?(@hit_sound_length)
      if @num_particles
        @num_particles.times do |i|
          Particle.new(@x,@y, 'media/fireball.png', -@particle_max_speed+rand*(@particle_max_speed*2), -@particle_max_speed+rand*(@particle_max_speed*2), rand*360, -0.5+rand*1.0, @particle_size, 0.1)
        end
        @num_particles.times do |i|
          Particle.new(@x,@y, 'media/smoke.png', 0.1, 0.1, rand*360, -0.5+rand*1.0, 1, 0.1, 2, :additive, @smoke_color || 0xff7f7f7f)
        end
        @map.add_crater(@x, @y, @splash_radius) if @dies and @splash_radius > 0
      end
      @dead = true
      DebugOut.out self.class.to_s + " died."
      @window.missile_die(self)
      return true
    end
    false
  end
  def disappear
    @window.missile_disappear(self)
  end
  
  def draw
    @image.draw_rot(@x, @window.height - @y, ZOrder::Missiles, @angle.to_i, 0.5, 0.5, 1, 1, @color) if @image
  end
end

class GrenadeLauncherRound < Missile
  def initialize(window, unit, x, y, angle, color, speed)
    @lethality_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 5, true, 80, GrenadeLauncher_SPLASH_RADIUS, false, true
    @num_particles, @particle_size, @particle_max_speed = 4, 1, 2.5
    @smoke_color = 0xff7f7f7f
    @landings = 0
    @image = Images["media/rocket.png"]
    @width, @height = @image.width, @image.height
    @hit_sound = "media/explode#{rand(6) + 1}.wav"
    super(window, unit, x, y, speed, angle, color)
  end
  
  def self.splash_radius?
    GrenadeLauncher_SPLASH_RADIUS
  end
end

class PoisonGrenadeLauncherRound < GrenadeLauncherRound
  def initialize(window, unit, x, y, angle, color, speed)
    super(window, unit, x, y, angle, color, speed)
    @smoke_color = 0xff3f7f3f
    @poison = true
  end
  
  def self.splash_radius?
    GrenadeLauncher_SPLASH_RADIUS
  end
  
    def die
    ret = super
    if ret
      PoisonCloud.new(@window, @unit, @x, @y)
    end
    ret
  end
end

class RifleRound < Missile
  def initialize(window, unit, x, y, angle, color, speed)
    @lethality_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 2, true, 10, 0, false, false
    @num_particles, @particle_size, @particle_max_speed = 0, 0, 0
    @landings = 0
    @image = Images["media/rifle_round.png"]
    @width, @height = @image.width, @image.height
    @hit_sound = nil ###
    super(window, unit, x, y, speed, angle, color)
  end
end

class Grenade < Missile
  def initialize(window, unit, x, y, angle, color, speed, explode_time = 100)
    @explode_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = explode_time, true, 100, 40, true, true
    @num_particles, @particle_size, @particle_max_speed = 5, 1.5, 3
    @landings, @bounciness, @explodes_on_timer = 3, 0.5, true
    @image = Images["media/grenade.png"]
    @width, @height = @image.width, @image.height
    @spin = rand(20) - 10
    @hit_sound = "media/explode#{rand(6) + 1}.wav"
    @hit_sound_length = 0.5
    super(window, unit, x, y, speed, angle, color)
  end
end

class ClusterGrenade < Missile
  def initialize(window, unit, x, y, angle, color, speed)
    @explode_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 50, true, 100, 40, true, true
    @num_particles, @particle_size, @particle_max_speed = 5, 1.5, 3
    @landings, @bounciness, @explodes_on_timer = 3, 0.5, true
    @image = Images["media/grenade.png"]
    @width, @height = @image.width, @image.height
    @spin = rand(20) - 10
    @hit_sound = "media/explode#{rand(6) + 1}.wav"
    @hit_sound_length = 0.5
    super(window, unit, x, y, speed, angle, color)
  end
  
  def die
    if super
      5.times do
        Grenade.new(@window, @unit, @x, @y, rand(360) - 180, 0xffffffff, rand(10) + 10, 60)
      end
    end
  end
end

class Debris < Missile
  def initialize(window, unit, x, y, angle, color, speed)
    @lethality_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 5, true, 10, 15, true, true
    @num_particles, @particle_size, @particle_max_speed = 3, 0.5, 1
    @landings = 0
    @image = Images["media/debris#{rand(4) + 1}.png"]
    @width, @height = @image.width, @image.height
    @spin = rand(20) - 10
    @hit_sound = "media/explode#{rand(6) + 1}.wav"
    super(window, unit, x, y, speed, angle, color)
  end
end

class WeaponMissile < Missile
  def initialize(window, unit, x, y, angle, speed, image)
    @angle, @image = angle, image
    @lethality_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 1, false, 0, 0, true, true
    @num_particles, @particle_size, @particle_max_speed = 0, 0, 0
    @width, @height = image.width, image.height
    @spin = rand(20) - 10
    @hit_sound = nil
    super(window, unit, x, y, speed, @angle, 0xffffffff)
  end
  
  def hit_unit?
    false
  end
end

class Giblet < Missile
  def initialize(window, unit, x, y, angle, speed)
    @speedx = 0
    @angle = angle
    @lethality_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 1, false, 0, 0, true, true
    @num_particles, @particle_size, @particle_max_speed = 0, 0, 0
    @image = Images["media/giblet#{rand(3) + 1}.png"]
    @width, @height = @image.width, @image.height
    @spin = rand(20) - 10
    @hit_sound = nil
    super(window, unit, x, y, speed, @angle, 0xffaaaaaa)
  end
  
  def hit_unit?
    false
  end
end

class PoisonCloud < Missile
  def initialize(window, unit, x, y)
    @explode_time, @dies, @damage, @splash_radius, @free_spinning, @affected_by_gravity = 100, true, 1, 50, true, false
    @explodes_on_timer = true
    @image = nil
    @width, @height = 50, 50
    @spin = 0
    @poison = true
    super(window, unit, x, y, 0, 0, 0xffffffff)
    @dies_on_hit, @hit_frequency = false, 10
  end
end
